﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;

namespace QDCommon.Model
{
    public class ModelCopyHelper
    {
        public static Dictionary<string, Object> funcDic = new Dictionary<string, Object>();
        public static Dictionary<string, Object> CopyTofuncDic = new Dictionary<string, Object>();
        public static T Copy<T>(T from) where T:class ,new()
        {
            return GetCopyFunc<T>()(from);
        }
        public static void CopyTo<T>(T from,T to)where T:class 
        {
            var func = GetCopyToFunc<T>();
            func(from,to);
        }

        public static Action<T,T> GetCopyToFunc<T>()where T:class 
        {
            string key = typeof(T).FullName;

            if (!CopyTofuncDic.ContainsKey(key))
            {

                List<Expression> expressions = new List<Expression>();
                var type = typeof(T);


                ParameterExpression from = Expression.Parameter(type, "from");
                ParameterExpression to = Expression.Parameter(type, "from");
              
                foreach (var item in typeof(T).GetProperties())
                {


                    var setElement = Expression.Assign(Expression.Property(to, item), Expression.Property(from, item));
                    expressions.Add(setElement);


                }
               
                BlockExpression blocks = Expression.Block(new ParameterExpression[] {  }, expressions);
                Expression<Action<T, T>> func = Expression.Lambda<Action<T, T>>(blocks, new ParameterExpression[] { from,to });

                CopyTofuncDic[key] = func.Compile();

            }
            return CopyTofuncDic[key] as Action<T, T>;
        }
        public static Func<T, T> GetCopyFunc<T>()
        {
            string key = typeof(T).FullName;

            if (!funcDic.ContainsKey(key))
            {

                List<Expression> expressions = new List<Expression>();
                var type = typeof(T);


                ParameterExpression parameterLeftExpression = Expression.Parameter(type, "from");
                var returnLabel = Expression.Label(type);
                var result = Expression.Variable(type, "result");
               
                BinaryExpression setResult = Expression.Assign(result, Expression.New(type));
                expressions.Add(setResult);
                foreach (var item in typeof(T).GetProperties())
                {


                    var setElement = Expression.Assign(Expression.Property(result, item), Expression.Property(parameterLeftExpression, item));
                    expressions.Add(setElement);


                }
                GotoExpression ge = Expression.Return(returnLabel, result, type);
                var lbl = Expression.Label(returnLabel, Expression.New(type));
                expressions.Add(ge);
                expressions.Add(lbl);

                BlockExpression blocks = Expression.Block(new ParameterExpression[] { result }, expressions);
                Expression<Func<T, T>> func = Expression.Lambda<Func<T, T>>(blocks, new ParameterExpression[] { parameterLeftExpression });

                funcDic[key] = func.Compile();

            }
            return funcDic[key] as Func<T, T>;
        }
    }
}
